这一篇,我们看看 C# 中的枚举器和迭代器的基本概念。
之前我们说过可以使用 foreach 来遍历数组元素,本篇来讨论为什么数组可以使用 foreach 语句处理,我们可以还使用迭代器来使得自定义类型也可以使用 foreach。
枚举器和可枚举类型
为什么数组可以使用 foreach 呢?因为数组可以提供一个枚举器(enumerator)对象。枚举器对象可以依次返回数组元素。
获取一个对象的枚举器可以调用对象的 GetEnumerator 方法。实现了 GetEnumerator 方法的对象称为可枚举(enumerable)对象。
foreach 语句就是用来配合可枚举类型一起使用的,他会执行下列行为:
- 调用 GetEnumerator 方法获取对象的枚举器
- 从枚举器请求每一项作为迭代变量(iteration variable),我们可以读取该变量但不能改变
foreach (Type ValName in EnumerableObject) { }
|
IEnumerator
实现 IEnumerator 接口的枚举器包含三个函数成员:
- Current: 返回当前位置项的属性,只读
- MoveNext: 把枚举器位置前进到集合下一项的方法,返回布尔值,位置有效返回 true,无效(到达尾部)返回 false。枚举器原始位置在第一项之前,因此在使用 Current 之前必须先调用 MoveNext
- Reset: 位置重置为原始状态
下面代码与直接使用 foreach 产生的结果是一样的:
using System.Collections;
class Program { static void Main(string[] args) { int[] MyArr = { 1, 2, 3, 5, 6, 7 }; IEnumerator ie = MyArr.GetEnumerator(); while (ie.MoveNext()) { int current = (int)ie.Current; Console.WriteLine(current); } } }
|
IEnumerable
可枚举类型是指实现了 IEnumerable 接口的类。IEnumerable 只有一个函数成员:
- GetEnumerator: 获取可枚举类型的枚举器
using System.Collections;
class MyClass : IEnumerable { public IEnumerator GetEnumerator() { } }
|
使用 IEnumerator 和 IEnumerable 示例
using System.Collections;
class ColorEnumerator : IEnumerator { private string[] _colors; private int _position = -1; public ColorEnumerator(string[] colors) { _colors = new string[colors.Length]; for (int i = 0; i < colors.Length; i++) { _colors[i] = colors[i]; } }
public object Current { get { if (_position <= -1 || _position >= _colors.Length) { throw new InvalidOperationException(); } return _colors[_position]; } }
public bool MoveNext(() { if (_position < _colors.Length - 1) { _position++; return true; } return false; }
public void Reset() { _position = -1; } }
class Colors : IEnumerable { private string[] _colors; public Colors(string[] colors) { _colors = new string[colors.Length]; for (int i = 0; i < colors.Length; i++) { _colors[i] = colors[i]; } }
public IEnumerator GetEnumerator() { return new ColorEnumerator(_colors); } }
class Progeam { static void Main() { Colors colors = new Colors(new string[] {"red", "green", "blue", "yellow"}); foreach (string color in colors) { Console.WriteLine(color); } } }
|
泛型枚举接口
之前我们写的都是非泛型版本,实际工作中,我们基本都使用泛型版本的 IEnumerator 和 IEnumerable 。非泛型版本只是兼任 2.0 版本之前无泛型的遗留代码。
泛型与非泛型版本的主要区别是:
- IEnumerable 接口的 GetEnumerator 方法要返回实现 IEnumerator 接口的枚举器实例
- 泛型版本的 Current 属性返回的不是 object 类型,而是实际类型的对象
迭代器
C# 2.0 之后,提供了更简单的创建枚举器和可枚举类型的方式。这种结构称为迭代器(iterator)。
- 迭代器返回一个泛型的枚举器
- yield return 语句声明这是枚举的下一项
public IEnumerator<string> BlackAndWhite() { yield return "black"; yield return "gray"; yield return "white"; }
|